﻿using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.IO;
using System.Xml.Schema;
using TLM.VsmdiReader.Exceptions;

namespace TLM.VsmdiReader.Test
{
    /// <summary>
    /// This class contains test methods for the VsmdiReader class
    /// </summary>
    [TestClass]
    public class VsmdiReaderTest
    {
        string _testListsPath;

        [TestInitialize]
        public void InitializeVariables()
        {
            string assemblyLocation = System.Reflection.Assembly.GetExecutingAssembly().Location;
            _testListsPath = System.IO.Path.GetDirectoryName(assemblyLocation);
        }

        /// <summary>
        /// Test the constructor with a not existing file.
        /// When the file is not exist  an exception of type FileNotFoundException needs to be thrown
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(FileNotFoundException))]
        public void VsmdiReader_Constructor_NotExistingFile_Test()
        {
            // Arrange
            string notExistingFile = Path.Combine(_testListsPath, @"VsmdiTestData\ThisFileNotExists.vsmdi");
           
            // Act
            new VsmdiParser(notExistingFile);

            // Assert
            Assert.Fail("Exception expected");
        }

        /// <summary>
        /// Test the ValidateVsmdi method with an invalid vsmdi file. 
        /// When the vsmdi is invalid (according to the xsd) an exception of type XmlSchemaValidationException needs to be thrown
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(XmlSchemaValidationException))]
        public void VsmdiReader_ValidateVsmdi_InvalidVsmdiFile_Test()
        {
            // Arrange
            string invalidVsmdiPath = Path.Combine(_testListsPath, @"VsmdiTestData\InvalidVsmdi.vsmdi");
            VsmdiParser parser = new VsmdiParser(invalidVsmdiPath);

            // Act
            parser.ValidateVsmdi();

            // Assert
            Assert.Fail("Exception expected");
        }

        /// <summary>
        /// Test the ValidateVsmdi method with an invalid vsmdi file. 
        /// When the vsmdi is invalid (according to the xsd) an exception of type XmlSchemaValidationException needs to be thrown.
        /// This test catch the exception and checks the message containing "InvalidElement". This an element in the invalid file that raises the exception.
        /// </summary>
        [TestMethod]
        public void VsmdiReader_ValidateVsmdi_InvalidVsmdiFile_ChecksMessage_Test()
        {
            // Arrange
            string invalidVsmdiPath = Path.Combine(_testListsPath, @"VsmdiTestData\InvalidVsmdi.vsmdi");
            VsmdiParser parser = new VsmdiParser(invalidVsmdiPath);
            try
            {
                // Act
                parser.ValidateVsmdi();
            }
            catch (XmlSchemaValidationException ex)
            {
                // Assert
                Assert.IsTrue(ex.Message.Contains("InvalidElement"));
            }
        }

        /// <summary>
        /// Test the ValidateVsmdi method with a valid vsmdi file. 
        /// </summary>
        [TestMethod]
        public void VsmdiReader_ValidateVsmdi_ValidVsmdiFile_Test()
        {
            // Arrange
            string validVsmdiPath = Path.Combine(_testListsPath, @"VsmdiTestData\FourTestLinks.vsmdi");
            VsmdiParser parser = new VsmdiParser(validVsmdiPath);

            // Act
            bool isValid = parser.ValidateVsmdi();

            // Assert
            Assert.IsTrue(isValid);
        }

        /// <summary>
        /// Test the ParseVsmdiFile method with a (invalid) vsmdi file that not contains the root test list (ListOfTests).
        /// When ListOfTests is missing an exception of type ListOfTestsNotFoundException needs to be  thrown.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ListOfTestsNotFoundException))]
        public void VsmdiReader_ParseVsmdiFile_WithoutListOfTests_Test()
        {
            // Arrange
            string withListOfTestsTestListVsmdiPath = Path.Combine(_testListsPath, @"VsmdiTestData\NoListsOfTests.vsmdi");
            VsmdiParser parser = new VsmdiParser(withListOfTestsTestListVsmdiPath);

            // Act
            parser.ParseVsmdiFile();

            // Assert
            Assert.Fail("Exception expected");
        }

        /// <summary>
        /// Test the ParseVsmdiFile method with a (invalid) vsmdi file contain a test link without id
        /// When the id attribute of a test list element is missing an exception of type MissingAttributeException needs to be thrown.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(MissingAttributeException))]
        public void VsmdiReader_ParseVsmdiFile_MissingTestListId_Test()
        {
            // Arrange
            string missingTestListId = Path.Combine(_testListsPath, @"VsmdiTestData\MissingTestListId.vsmdi");
            VsmdiParser parser = new VsmdiParser(missingTestListId);

            // Act
            parser.ParseVsmdiFile();

            // Assert
            Assert.Fail("Exception expected");
        }

        /// <summary>
        /// Test the ParseVsmdiFile method with a (invalid) vsmdi file contain a test link without id
        /// When the name attribute of a test list element is missing an exception of type MissingAttributeException needs to be thrown.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(MissingAttributeException))]
        public void VsmdiReader_ParseVsmdiFile_MissingTestListName_Test()
        {
            // Arrange
            string missingTestListName = Path.Combine(_testListsPath, @"VsmdiTestData\MissingTestListName.vsmdi");
            VsmdiParser parser = new VsmdiParser(missingTestListName);

            // Act
            parser.ParseVsmdiFile();

            // Assert
            Assert.Fail("Exception expected");
        }


        /// <summary>
        /// Test the ParseVsmdiFile method with a (invalid) vsmdi file contain a test link without id
        /// When the id attribute of a test link element is missing an exception of type MissingAttributeException needs to be thrown.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(MissingAttributeException))]
        public void VsmdiReader_ParseVsmdiFile_MissingTestLinkId_Test()
        {
            // Arrange
            string missingTestLinkId = Path.Combine(_testListsPath, @"VsmdiTestData\MissingTestLinkId.vsmdi");
            VsmdiParser parser = new VsmdiParser(missingTestLinkId);

            // Act
            parser.ParseVsmdiFile();

            // Assert
            Assert.Fail("Exception expected");
        }

        /// <summary>
        /// Test the ParseVsmdiFile method with a vsmdi empty file that does contains the root test list (ListOfTests).
        /// </summary>
        [TestMethod]       
        public void VsmdiReader_ParseVsmdiFile_EmptyTestList_WithListOfTests_Test()
        {
            // Arrange
            string emptyTestListVsmdiPath = Path.Combine(_testListsPath, @"VsmdiTestData\Empty.vsmdi");
            VsmdiParser parser = new VsmdiParser(emptyTestListVsmdiPath);

            // Act
            VsmdiFile actualVsmdiFile = parser.ParseVsmdiFile();

            // Assert
            Assert.IsNotNull(actualVsmdiFile);
            Assert.AreEqual("Empty.vsmdi", actualVsmdiFile.FileName);

            Assert.IsNotNull(actualVsmdiFile.ListOfTests);

            Assert.IsNotNull(actualVsmdiFile.ListOfTests.ChildLists);
            Assert.IsNotNull(actualVsmdiFile.ListOfTests.TestLinks);

            int actualChildLists = actualVsmdiFile.ListOfTests.ChildLists.Count();
            Assert.AreEqual(0, actualChildLists);

            int actualTestLinkCount = actualVsmdiFile.ListOfTests.TestLinks.Count();
            Assert.AreEqual(0, actualTestLinkCount);
        }

        /// <summary>
        /// Test the ParseVsmdiFile method with a vsmdi file that contains one (child) test list with four test links.
        /// One of the test links is disabled)
        /// 
        /// </summary>
        [TestMethod]
        public void VsmdiReader_ParseVsmdiFile_TestListWithFourTestLinks_Test()
        {
            // Arrange
            string filledTestListVsmdiPath = Path.Combine(_testListsPath, @"VsmdiTestData\FourTestLinks.vsmdi");
            VsmdiParser parser = new VsmdiParser(filledTestListVsmdiPath);

            // Act
            VsmdiFile actualVsmdiFile = parser.ParseVsmdiFile();

            // Assert
            Assert.IsNotNull(actualVsmdiFile);
            Assert.AreEqual("FourTestLinks.vsmdi", actualVsmdiFile.FileName);

            Assert.IsNotNull(actualVsmdiFile.ListOfTests);

            Assert.IsNotNull(actualVsmdiFile.ListOfTests.ChildLists);
            Assert.IsNotNull(actualVsmdiFile.ListOfTests.ChildLists.First());

            Assert.IsNotNull(actualVsmdiFile.ListOfTests.ChildLists.First().TestLinks);
            Assert.IsNotNull(actualVsmdiFile.ListOfTests.ChildLists.First().TestLinks.First());

            List<TestList> actualChildLists = actualVsmdiFile.ListOfTests.ChildLists.ToList();
            Assert.AreEqual(1, actualChildLists.Count);

            //Check if the child list (called AllTests) is parsed correctly
            Assert.AreEqual("AllTests", actualChildLists[0].Name);
            Assert.AreEqual("fd7a6237-9fc6-4569-83a9-44a6dd96d8e9", actualChildLists[0].Id.ToString());

            List<TestLink> actualTestLinks = new List<TestLink>(actualChildLists[0].TestLinks);
            Assert.AreEqual(4, actualTestLinks.Count);

            //Check if all the test links are present
            Assert.IsTrue(actualTestLinks.Any(x => x.Name.Equals("Test1")));
            Assert.IsTrue(actualTestLinks.Any(x => x.Name.Equals("Test2")));
            Assert.IsTrue(actualTestLinks.Any(x => x.Name.Equals("Test3")));
            Assert.IsTrue(actualTestLinks.Any(x => x.Name.Equals("Test4")));

            //Check if the correct test link is disabled
            Assert.IsFalse(actualTestLinks.Single(x => x.Name.Equals("Test4")).Enabled);
        }

        /// <summary>
        /// Test the ParseVsmdiFile method with a vsmdi file that contain nested test lists (3 levels)
        /// Check if all levels contain the correct test links.
        /// </summary>
        [TestMethod]
        public void VsmdiReader_ParseVsmdiFile_NestedTestLists_Test()
        {
            // Arrange
            string nestedTestListVsmdiPath = Path.Combine(_testListsPath,  @"VsmdiTestData\NestedLists.vsmdi");
            VsmdiParser parser = new VsmdiParser(nestedTestListVsmdiPath);

            // Act
            VsmdiFile actualVsmdiFile = parser.ParseVsmdiFile();

            // Assert
            Assert.IsNotNull(actualVsmdiFile);
            Assert.AreEqual("NestedLists.vsmdi", actualVsmdiFile.FileName);

            Assert.IsNotNull(actualVsmdiFile.ListOfTests);

            List<TestList> actualTestLists = actualVsmdiFile.ListOfTests.ChildLists.ToList();
            List<TestList> actualTestListsLvl2 = actualTestLists[0].ChildLists.ToList();
            List<TestList> actualTestListsLvl3 = actualTestListsLvl2[0].ChildLists.ToList();

            List<TestLink> actualTestLinks = actualTestLists[0].TestLinks.ToList();
            List<TestLink> actualTestLinksLvl2 = actualTestListsLvl2[0].TestLinks.ToList();
            List<TestLink> actualTestLinksLvl3 = actualTestListsLvl3[0].TestLinks.ToList();

            Assert.AreEqual(4, actualTestLinks.Count);
            Assert.AreEqual(3, actualTestLinksLvl2.Count);
            Assert.AreEqual(2, actualTestLinksLvl3.Count);

            Assert.IsTrue(actualTestLinks.Any(x => x.Name.Equals("Test1")));
            Assert.IsTrue(actualTestLinks.Any(x => x.Name.Equals("Test2")));
            Assert.IsTrue(actualTestLinks.Any(x => x.Name.Equals("Test3")));
            Assert.IsTrue(actualTestLinks.Any(x => x.Name.Equals("Test4")));

            Assert.IsTrue(actualTestLinksLvl2.Any(x => x.Name.Equals("Test11")));
            Assert.IsTrue(actualTestLinksLvl2.Any(x => x.Name.Equals("Test22")));
            Assert.IsTrue(actualTestLinksLvl2.Any(x => x.Name.Equals("Test33")));

            Assert.IsTrue(actualTestLinksLvl3.Any(x => x.Name.Equals("Test111")));
            Assert.IsTrue(actualTestLinksLvl3.Any(x => x.Name.Equals("Test222")));
        }

        /// <summary>
        /// Test the ParseVsmdiFile method with a vsmdi file that contains a test list with a not existing parent
        /// The level-3 test list has a not existing parent. This test list needs to be ignored
        /// </summary>
        [TestMethod]
        public void VsmdiReader_ParseVsmdiFile_MissingParentTestList_Test()
        {
            // Arrange
            string missingParentTestListVsmdiPath = Path.Combine(_testListsPath, @"VsmdiTestData\MissingParent.vsmdi");
            VsmdiParser parser = new VsmdiParser(missingParentTestListVsmdiPath);

            // Act
            VsmdiFile actualVsmdiFile = parser.ParseVsmdiFile();

            // Assert
            Assert.IsNotNull(actualVsmdiFile);
            Assert.AreEqual("MissingParent.vsmdi", actualVsmdiFile.FileName);

            Assert.IsNotNull(actualVsmdiFile.ListOfTests);

            List<TestList> actualTestLists = actualVsmdiFile.ListOfTests.ChildLists.ToList();
            List<TestList> actualTestListsLvl2 = actualTestLists[0].ChildLists.ToList();
            List<TestList> actualTestListsLvl3 = actualTestListsLvl2[0].ChildLists.ToList();

            List<TestLink> actualTestLinks = actualTestLists[0].TestLinks.ToList();
            List<TestLink> actualTestLinksLvl2 = actualTestListsLvl2[0].TestLinks.ToList();

            Assert.AreEqual(4, actualTestLinks.Count);
            Assert.AreEqual(3, actualTestLinksLvl2.Count);
            Assert.AreEqual(0, actualTestListsLvl3.Count);

            Assert.IsTrue(actualTestLinks.Any(x => x.Name.Equals("Test1")));
            Assert.IsTrue(actualTestLinks.Any(x => x.Name.Equals("Test2")));
            Assert.IsTrue(actualTestLinks.Any(x => x.Name.Equals("Test3")));
            Assert.IsTrue(actualTestLinks.Any(x => x.Name.Equals("Test4")));

            Assert.IsTrue(actualTestLinksLvl2.Any(x => x.Name.Equals("Test11")));
            Assert.IsTrue(actualTestLinksLvl2.Any(x => x.Name.Equals("Test22")));
            Assert.IsTrue(actualTestLinksLvl2.Any(x => x.Name.Equals("Test33")));
        }    
    }
}
